﻿using System;
using System.Net.Sockets;
using System.Threading;
using HJ212_Common;
using SailHero.Soft.ESB.Communication.ClientAndServer.Communication;
using SailHero.Soft.ESB.Communication.ClientAndServer.Communication.Channels;
using SailHero.Soft.ESB.Communication.ClientAndServer.Communication.EndPoints;
using SailHero.Soft.ESB.Communication.ClientAndServer.Communication.Messages;
using SailHero.Soft.ESB.Communication.ClientAndServer.Communication.Protocols;

namespace SailHero.Soft.ESB.Communication.ClientAndServer.Server
{
    /// <summary>
    /// This class represents a client in server side.
    /// </summary>
    internal class ServerClient : IServerClient
    {
        #region Public events

        /// <summary>
        /// This event is raised when a new message is received.
        /// </summary>
        public event EventHandler<MessageEventArgs> MessageReceived;

        /// <summary>
        /// This event is raised when a new message is sent without any error.
        /// It does not guaranties that message is properly handled and processed by remote application.
        /// </summary>
        public event EventHandler<MessageEventArgs> MessageSent;

        /// <summary>
        /// 发送数据超时的事件
        /// </summary>
        public event EventHandler<MessageEventArgs> MessageSentTimeOut;


        public log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// This event is raised when a new Client login after Connect without any error.
        /// It does not guaranties that message is properly handled and processed by remote application.
        /// </summary>
        public event EventHandler<MessageEventArgs> ClientLogin;

        private string SysID = ConfigOprater.GetNodeValue("SysID");
        /// <summary>
        /// This event is raised when client is disconnected from server.
        /// </summary>
        public event EventHandler Disconnected;

        #endregion

        #region Public properties

        /// <summary>
        /// Unique identifier for this client in server.
        /// </summary>
        public string ClientId { get; set; }

        /// <summary>
        /// 客户端的唯一编号
        /// </summary>
        public string MN
        {
            get;
            set;
        }
        /// <summary>
        /// 客户端的唯一编号
        /// </summary>
        public string PW
        {
            get;
            set;
        }

        /// <summary>
        /// 超时重发的次数，按212协议标准来说，每隔15分钟一次登录，
        /// 2次之后还没登录成功，则复位通讯模块，即在服务端强制关闭Socket链接
        /// </summary>
        public int DefautltReSendTimes
        {
            get;
            set;
        }

        /// <summary>
        /// 考虑可能212协议默认15分钟时间过长的问题
        /// 增加新的链路联通性测试包频率计数器
        /// </summary>
        public int ReSendTimes
        {
            get;
            set;
        }


        /// <summary>
        /// Gets the communication state of the Client.
        /// </summary>
        public CommunicationStates CommunicationState
        {
            get
            {
                return _communicationChannel.CommunicationState;
            }
        }

        /// <summary>
        /// Gets/sets wire protocol that is used while reading and writing messages.
        /// </summary>
        public IWireProtocol WireProtocol
        {
            get { return _communicationChannel.WireProtocol; }
            set { _communicationChannel.WireProtocol = value; }
        }

        ///<summary>
        /// Gets endpoint of remote application.
        ///</summary>
        public ESBEndPoint RemoteEndPoint
        {
            get { return _communicationChannel.RemoteEndPoint; }
        }

        /// <summary>
        /// Gets the time of the last succesfully received message.
        /// </summary>
        public DateTime LastReceivedMessageTime
        {
            get
            {
                return _communicationChannel.LastReceivedMessageTime;
            }
        }

        /// <summary>
        /// Gets the time of the last succesfully received message.
        /// </summary>
        public DateTime LastSentMessageTime
        {
            get
            {
                return _communicationChannel.LastSentMessageTime;
            }
        }


        public Socket ClientSocket
        {
            get
            {
                return _communicationChannel.ClientSocket;
            }

        }

        /// <summary>
        /// 登录超时时间，以分钟为单位
        /// </summary>
        public int LoginTimeOut { get; set; }

        #endregion

        #region Private fields

        private const string pingQN = "20000000000000001";
        /// <summary>
        /// The communication channel that is used by client to send and receive messages.
        /// </summary>
        private readonly ICommunicationChannel _communicationChannel;

        /// <summary>
        /// This timer is used to send PingMessage messages to server periodically.
        /// </summary>
        private readonly System.Timers.Timer _pingTimer;

        /// <summary>
        /// This timer is used to send PingMessage messages to server periodically.
        /// </summary>
        private readonly System.Timers.Timer _manpingTimer;

        private readonly Object thisLock = new Object();
        private readonly Object LockObj = new Object();
        #endregion

        #region Constructor

        /// <summary>
        /// Creates a new ScsClient object.
        /// </summary>
        /// <param name="communicationChannel">The communication channel that is used by client to send and receive messages</param>
        public ServerClient(ICommunicationChannel communicationChannel)
        {

            communicationChannel.ClientSocket.SetKeepAlive();

            _communicationChannel = communicationChannel;
            _communicationChannel.MessageReceived += CommunicationChannel_MessageReceived;
            _communicationChannel.MessageSentTimeOut += _communicationChannel_MessageSentTimeOut;
            _communicationChannel.MessageSent += CommunicationChannel_MessageSent;
            _communicationChannel.Disconnected += CommunicationChannel_Disconnected;
            _pingTimer = new System.Timers.Timer();
            _pingTimer.Interval = 15 * 60 * 1000; //15分钟

            if (LoginTimeOut >= 1 && LoginTimeOut <= 60)
            {

                _pingTimer.Interval = (LoginTimeOut / 2) * 60 * 1000; //15分钟
            }
            _pingTimer.Elapsed += PingTimer_Elapsed;
            _pingTimer.Start();

            _manpingTimer = new System.Timers.Timer();
            _manpingTimer.Interval = 30 * 1000;//30秒钟 保活长连接
            _manpingTimer.Elapsed += _manpingTimer_Elapsed;
            _manpingTimer.Start();
        }




        public ServerClient(ICommunicationChannel communicationChannel, int _loginTimeOut)
        {
            _communicationChannel = communicationChannel;
            _communicationChannel.MessageReceived += CommunicationChannel_MessageReceived;
            _communicationChannel.MessageSent += CommunicationChannel_MessageSent;
            _communicationChannel.Disconnected += CommunicationChannel_Disconnected;
            _communicationChannel.MessageSentTimeOut += _communicationChannel_MessageSentTimeOut;
            _pingTimer = new System.Timers.Timer();
            _pingTimer.Interval = 15 * 60 * 1000; //15分钟
            LoginTimeOut = _loginTimeOut;
            if (LoginTimeOut >= 1 && LoginTimeOut <= 60)
            {
                _pingTimer.Interval = (LoginTimeOut / 2) * 60 * 1000; //15分钟
            }
            _pingTimer.Elapsed += PingTimer_Elapsed;
            _pingTimer.Start();

            _manpingTimer = new System.Timers.Timer();
            _manpingTimer.Interval = 30 * 1000;//30秒钟 保活长连接
            _manpingTimer.Elapsed += _manpingTimer_Elapsed;
            _manpingTimer.Start();
        }



        #endregion

        #region Public methods
        /// <summary>
        /// 根据不同的参数，停止不同的定时器
        /// 1 停止Server ping
        /// 2 停止监测客户端15分钟登录包
        /// </summary>
        /// <param name="h">参数</param>
        public void StopPing(int h)
        {
            switch (h)
            {
                case 1:
                    _manpingTimer.Stop();
                    _manpingTimer.Dispose();
                    lock (LockObj)
                    {
                        ReSendTimes = 0;
                    }
                    break;
                case 2:
                    _pingTimer.Stop();
                    _pingTimer.Dispose();
                    lock (thisLock)
                    {
                        DefautltReSendTimes = 0;
                    }
                    break;
                case 3:
                    _manpingTimer.Stop();
                    _manpingTimer.Dispose();
                    lock (LockObj)
                    {
                        ReSendTimes = 0;
                    }
                    _pingTimer.Stop();
                    _pingTimer.Dispose();
                    lock (thisLock)
                    {
                        DefautltReSendTimes = 0;
                    }
                    GC.Collect();
                    break;
            }



        }

        /// <summary>
        /// Disconnects from client and closes underlying communication channel.
        /// </summary>
        public void Disconnect()
        {
            StopPing(3);
            _communicationChannel.Disconnect();
        }

        /// <summary>
        /// Sends a message to the client.
        /// </summary>
        /// <param name="message">Message to be sent</param>
        public void SendMessage(IMessage message)
        {
            _communicationChannel.SendMessage(message);
        }

        #endregion

        #region Private methods
        /// <summary>
        /// Handles Elapsed event of _pingTimer to send PingMessage messages to server.
        /// </summary>
        /// <param name="sender">Source of event</param>
        /// <param name="e">Event arguments</param>
        private void _manpingTimer_Elapsed(object sender, EventArgs e)
        {
            GoPing();
        }

        private void GoPing()
        {
            try
            {
                ThreadStart Thsrd = new ThreadStart(PingClient);
                Thread Thrd = new Thread(Thsrd);
                Thrd.Start();
            }
            catch (Exception ex)
            {
                log.Error("通信出现异常", ex);
            }
        }
        private void PingClient()
        {
            try
            {
                lock (LockObj)
                {
                    ReSendTimes += 1;
                }
                string cmd = "QN={0};ST={1};CN=1011;PW={2};MN={3};Flag=1;CP=&&&&";//提取现场机时间
                cmd = string.Format(cmd, pingQN, SysID, this.PW, this.MN);
                //服务端收到ping心跳包之后回传ping心跳包给客户端，形成消息通道回路
                TextMessage txtmsg = new TextMessage(cmd);
                txtmsg.Text = cmd;
                _communicationChannel.SendMessage(txtmsg);
            }
            catch (Exception ex)
            {
                log.Error("通信出现异常", ex);
            }
        }

        /// <summary>
        /// Handles Elapsed event of _pingTimer to send PingMessage messages to server.
        /// </summary>
        /// <param name="sender">Source of event</param>
        /// <param name="e">Event arguments</param>
        private void PingTimer_Elapsed(object sender, EventArgs e)
        {
            GoConnectCheck();
        }
        /// <summary>
        /// 执行链路联通性超时监测
        /// </summary>
        private void GoConnectCheck()
        {


            try
            {
                ThreadStart Thsrd = new ThreadStart(AddTimes);
                Thread Thrd = new Thread(Thsrd);
                Thrd.Start();
            }
            catch (Exception ex)
            {
                log.Error("通信出现异常", ex);
            }
        }
        /// <summary>
        /// 超时次数加1
        /// </summary>
        private void AddTimes()
        {
            lock (thisLock)
            {
                DefautltReSendTimes += 1;
            }
        }
        /// <summary>
        /// Handles Disconnected event of _communicationChannel object.
        /// </summary>
        /// <param name="sender">Source of event</param>
        /// <param name="e">Event arguments</param>
        private void CommunicationChannel_Disconnected(object sender, EventArgs e)
        {
            StopPing(3);
            OnDisconnected();
        }

        /// <summary>
        /// Handles MessageReceived event of _communicationChannel object.
        /// </summary>
        /// <param name="sender">Source of event</param>
        /// <param name="e">Event arguments</param>
        private void CommunicationChannel_MessageReceived(object sender, MessageEventArgs e)
        {
            ///todo：处理心跳包
            try
            {
                TextMessage message = (TextMessage)e.Message;

                if (message.CN == "9021")//注册登录包
                {
                    lock (thisLock)
                    {
                        this.DefautltReSendTimes = 0;
                    }
                    string cmd = "ST=91;CN=9022;PW={0};MN={1};Flag=0;CP=&&QN={2};Logon=1&& ";
                    cmd = string.Format(cmd, message.PW, message.MN, message.QN);
                    this.MN = message.MN.Trim();
                    this.PW = message.PW.Trim();
                    OnLogin(e.Message);
                    //服务端收到ping心跳包之后回传ping心跳包给客户端，形成消息通道回路
                    TextMessage txtmsg = new TextMessage() { RepliedMessageId = message.MessageId };
                    txtmsg.Text = cmd;
                    txtmsg.MN = this.MN;
                    txtmsg.PW = this.PW;
                    txtmsg.QN = message.QN;
                    _communicationChannel.SendMessage(txtmsg);
                    return;
                }
                if (string.Format("{0}", message.QN).Trim() == pingQN)//server to Client ping package
                {
                    lock (LockObj)
                    {
                        this.ReSendTimes = 0;
                    }
                    return;
                }
                lock (LockObj)
                {
                    this.ReSendTimes = 0;
                }
                lock (thisLock)
                {
                    this.DefautltReSendTimes = 0;
                }
                OnMessageReceived(message);
            }
            catch (Exception ex)
            {
                log.Error("通信出现异常", ex);
            }
        }

        /// <summary>
        /// Handles MessageSent event of _communicationChannel object.
        /// </summary>
        /// <param name="sender">Source of event</param>
        /// <param name="e">Event arguments</param>
        private void CommunicationChannel_MessageSent(object sender, MessageEventArgs e)
        {
            OnMessageSent(e.Message);
        }
        public void _communicationChannel_MessageSentTimeOut(object sender, MessageEventArgs e)
        {
            OnMessageSentTimeOut(e.Message);
        }
        #endregion

        #region Event raising methods

        /// <summary>
        /// Raises Disconnected event.
        /// </summary>
        private void OnDisconnected()
        {
            var handler = Disconnected;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }



        /// <summary>
        /// Raises MessageReceived event.
        /// </summary>
        /// <param name="message">Received message</param>
        private void OnMessageReceived(IMessage message)
        {
            var handler = MessageReceived;
            if (handler != null)
            {
                handler(this, new MessageEventArgs(message));
            }
        }

        /// <summary>
        /// Raises MessageSent event.
        /// </summary>
        /// <param name="message">Received message</param>
        protected virtual void OnMessageSent(IMessage message)
        {
            var handler = MessageSent;
            if (handler != null)
            {
                handler(this, new MessageEventArgs(message));
            }
        }
        /// <summary>
        /// Raises MessageSentTimeOut event.
        /// </summary>
        /// <param name="message">Sended message</param>
        protected virtual void OnMessageSentTimeOut(IMessage message)
        {
            var handler = MessageSentTimeOut;
            if (handler != null)
            {
                handler(this, new MessageEventArgs(message));
            }
        }
        /// <summary>
        /// Raises ClientLogin event.
        /// </summary>
        /// <param name="message">Received message</param>
        protected virtual void OnLogin(IMessage message)
        {

            var handler = ClientLogin;
            if (handler != null)
            {
                handler(this, new MessageEventArgs(message));
            }
        }
        #endregion



    }
}